package org.vaadin.vaadinjit;

import java.lang.reflect.Method;
import java.util.Map;

import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;

import org.vaadin.vaadinjit.widgetset.client.ui.VJitAreaChart;

/**
 * JIT AreaChart UI component.
 * 
 * @author jonkka
 *
 */
@com.vaadin.ui.ClientWidget(VJitAreaChart.class)
public class JitAreaChart extends JitChartComponent {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/** UIDL identifier for filtering stack  */
	protected static String FILTER_STACK_IDENTIFIER = "filterStack";
	
	private static final Method FILTER_LABEL_METHOD;
	
	private static final Method RESTORE_LABEL_METHOD;
	
	/** Tells repaint that it should restore the stacks */
	private boolean restoreStack = false;
	
	/** Tells repaint that it should filter a stack */
	private boolean filterStack = false;
	
	/** The label of the stack that should be or is filtered */
	private String filterStackLabel = null;

	/** This tells if the stacks can be filtered by clicks */
	private boolean filterOnClick = true;

	/** Tells repaint that it should update the filter on click value */
	private boolean filterOnClickUpdated = false;
	
	static {
		try {
			FILTER_LABEL_METHOD = FilterListener.class.getDeclaredMethod(
					"labelFilter", new Class[] {FilterEvent.class});
			
			RESTORE_LABEL_METHOD = RestoreListener.class.getDeclaredMethod(
					"labelRestore", new Class[] {RestoreEvent.class});
			
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(
					"Internal error finding methods in JitChartComponent");
		}
	}
	
	
	/**
	 * Creates a new JitAreaChart
	 * 
	 */
	public JitAreaChart() {
		super();
	}

	
	/**
	 * 
	 */
	@Override
	public void paintContent(PaintTarget target) throws PaintException {
		super.paintContent(target);
		
		if (filterStack) {
			target.addAttribute("filterStack", filterStackLabel);
			filterStack = false;
		}
		
		if (restoreStack) {
			target.addAttribute("restoreStack", true);
			restoreStack = false;
		}
		
		if (filterOnClickUpdated) {
			target.addAttribute("filterOnClick", filterOnClick);
		}
	}

	
	/**
	 * Receive and handle events and other variable changes from the client.
	 * 
	 * {@inheritDoc}
	 */
	@Override
	public void changeVariables(Object source, Map<String, Object> variables) {
		super.changeVariables(source, variables);
		
		if (variables.containsKey(FILTER_STACK_IDENTIFIER)) {
			String label = variables.get(FILTER_STACK_IDENTIFIER).toString();

			// Fire a hover label event
			fireEvent(new FilterEvent(this, label));
		}
	}

	

	
	
	/**
	 * This filters the selected stack, collapsing all other stacks.
	 * 
	 * @param stackLabel the stack label to filter
	 */
	public void filterStack(String stackLabel) {
		this.filterStackLabel = stackLabel;
		this.filterStack = true;
		this.requestRepaint();
		this.fireEvent(new FilterEvent(this, stackLabel));
	}
	
	
	/**
	 * Restores all stacks
	 */
	public void restoreStacks() {
		restoreStack = true;
		this.requestRepaint();
		this.fireEvent(new RestoreEvent(this));
	}
	
	
	/**
	 * Sets whether the stacks can be filtered by clicking on them.
	 * 
	 * @param value true for clickable
	 */
	public void setFilterOnClick(boolean value) {
		this.filterOnClick = value;
		this.filterOnClickUpdated = true;
		this.requestRepaint();
	}
	
	
	/**
	 * Tells if the stacks can be filtered by clicking on them.
	 * 
	 * @return true if the stacks can be filtered by clicks, false otherwise
	 */
	public boolean getFilterOnClick() {
		return filterOnClick;
	}
	
	
	/**
	 * Register a listener for filter events.
	 * 
	 * @param listener
	 */
	public void addListener(FilterListener listener) {
		addListener(FilterEvent.class, listener, FILTER_LABEL_METHOD);
	}
	
	
	/**
	 * Register a listener for restore events.
	 * 
	 * @param listener
	 */
	public void addListener(RestoreListener listener) {
		addListener(RestoreEvent.class, listener, RESTORE_LABEL_METHOD);
	}
	

	/**
	 * Listener for filter events.
	 * 
	 * @author jonkka
	 *
	 */
	public interface FilterListener {
		
		/**
		 * Fired when a label is filtered.
		 * 
		 * @param event
		 */
		public void labelFilter(FilterEvent event);
		
	}
	
	
	/**
	 * Listener for restore events
	 * 
	 * @author jonkka
	 *
	 */
	public interface RestoreListener {
		
		
		/**
		 * Fired when stacks are restored
		 * 
		 * @param event the event instance
		 */
		public void labelRestore(RestoreEvent event);
		
	}
	
	
	/**
	 * Filter stack event
	 * 
	 * @author jonkka
	 *
	 */
	public class FilterEvent extends Event {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private String label;
		
		public FilterEvent(JitAreaChart source, String label) {
			super(source);
			this.label = label;
		}
		
		/**
		 * Get the string of the filtered label
		 * 
		 * @return name of the filtered label
		 */
		public String getLabel() {
			return label;
		}
		
	}
	
	
	/**
	 * Restore stack event
	 * 
	 * @author jonkka
	 *
	 */
	public class RestoreEvent extends Event {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public RestoreEvent(JitAreaChart source) {
			super(source);
		}
	
		
	}
}
